// Basic demo for accelerometer readings from Adafruit MPU6050

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

// LED 2812
#include <NeoPixelBus.h>
const uint16_t PixelCount = 72; // 36; // this example assumes 4 pixels, making it smaller will cause a failure
const uint16_t PixelLength = PixelCount/6;  // how many pixels in each face
const uint8_t PixelPin = 2;  // make sure to set this to the correct pin, ignored for Esp8266

NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart1800KbpsMethod> strip(PixelCount, PixelPin);

int lastPos = -1;

int times[] = { 6000, 12000, 24000, 48000, 96000, 192000 };

RgbColor colors[] = { 
  RgbColor( 200,200, 0 ),
  RgbColor( 200, 0, 200 ),
  RgbColor( 0, 200, 200 ),
  RgbColor( 200, 0, 0 ),
  RgbColor( 0, 200, 0 ),
  RgbColor( 0, 0, 200 )
};

Adafruit_MPU6050 mpu;

bool started = false;
int face = -1;

int flag = 2000;

int lastFace = -1;
int lastFaceTime = 0;

int timeLeft = 0;
int totalTime = 0;
int timeStep = 500; // ms;
int fadeFlag = -1;
int fadeStep = 10;
int stepsRan = 0;
int lastStep = -1;

void fillAll(RgbColor c){
  for(int i=0; i<PixelCount; i++){ 
     if( started && ( i>= face * PixelLength && i< (face+1) * PixelLength ) ){
       // skip current running
     }else{      
       strip.SetPixelColor(i, c); 
     }
  }
  strip.Show();  
}

void doFlash(){  
    int beepPin = 0;
    pinMode(beepPin, OUTPUT);

    bool beep = false;
    
    // flash 
    for (int i=0; i<=5; i++){
      beep = i % 2 == 0;
      delay( 300 );
      fillAll( beep? colors[face] : RgbColor(0) );
      digitalWrite(beepPin, beep?HIGH : LOW);      
    }
}

void doFade(){
  if(!started) return;
  if( millis() < fadeFlag + 500 ) return;
  fadeFlag = millis();

  timeLeft -= timeStep;
  
  if(timeLeft<=0){
    started = false;
    strip.SetPixelColor(lastStep, RgbColor(0) ); 
    strip.Show();
      
    doFlash();
    face = -1;    
    return;
  } // time end

  int used = totalTime - timeLeft;  

  int i = used * PixelLength / totalTime ; // index of being faded
  
  i = i + face * PixelLength  ; 
  
  stepsRan++;
  if( lastStep != i ){
    strip.SetPixelColor(lastStep, RgbColor(0) );     
    lastStep = i;
    stepsRan=1;
  }
  
  RgbColor color = strip.GetPixelColor(i);
  color.Darken( fadeStep );   
  strip.SetPixelColor(i, color);     

  strip.Show();  
}

void showFace(int p){
  RgbColor on = RgbColor(10, 10, 0); 
  RgbColor off = RgbColor(0, 0, 0);
  for (int i = 0; i < PixelCount; i++)
  {
    if(  started && ( i>= face * PixelLength && i< (face+1) * PixelLength ) ){      
      // skip current face
    }else if(i>= p * PixelLength && i< (p+1) * PixelLength){ 
      strip.SetPixelColor(i, on); 
    }else{
      strip.SetPixelColor(i, off); 
    }
  }
  strip.Show(); 
}

void doStart(int p){

  if( p == lastPos ) return;
  lastPos = p;
  started = true;
  face = p;
   
  RgbColor on = colors[p]; 
  RgbColor off = RgbColor(0, 0, 0);
  for (int i = 0; i < PixelCount; i++)
  {
    if( i>= p * PixelLength && i< (p+1) * PixelLength){ 
      strip.SetPixelColor(i, on); 
    }else{      
      strip.SetPixelColor(i, off);
    } 
  }
  strip.Show();
  timeLeft = times[face];
  totalTime = timeLeft;
  fadeStep = 1 + timeStep * PixelLength * 250 / totalTime;
//  Serial.print("timeLeft=");  Serial.println(timeLeft);
//  Serial.print("face=");  Serial.println(face);
}

void setup(void) {
  Serial.begin(115200);

  Wire.begin(1,3); // ESP01-s only, SCL(RX), SDA(TX)

  strip.Begin();
  strip.Show();

  
  while (!Serial) {
    delay(10); // will pause Zero, Leonardo, etc until serial console opens
  }

  // Try to initialize!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);      
      Serial.println("Failed to find MPU6050 chip");
    }
  }

  mpu.setAccelerometerRange(MPU6050_RANGE_16_G);
  mpu.setGyroRange(MPU6050_RANGE_250_DEG);
  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
  Serial.println("");
  delay(100);
}

void loop() {

  /* Get new sensor events with the readings */
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  /* Print out the values */
//  Serial.print(a.acceleration.x);
//  Serial.print(",");
//  Serial.print(a.acceleration.y);
//  Serial.print(",");
//  Serial.print(a.acceleration.z);
//  Serial.print(", ");
//  Serial.print(g.gyro.x);
//  Serial.print(",");
//  Serial.print(g.gyro.y);
//  Serial.print(",");
//  Serial.print(g.gyro.z);

  // to find the face on top
  float vv[] = {a.acceleration.x, a.acceleration.y, a.acceleration.z};
  int j=-1;
  int v=-1;
  for ( int i=0; i<3; i++ ){
    if( abs( vv[i] ) > 8 ){
      j = i;
      break;
    }
  }

  if( j>=0 ){
    
    v=j*2;
    if( vv[j] < 0 ){
      v += 1;
    }

    if( lastFace != v ){
      lastFaceTime = millis();
      lastFace = v;
    }

    if( abs( vv[j] ) > 20 ){ // only start when shake hard
      doStart(v);    
    }else if(!started & v == lastFace && millis()> (lastFaceTime + 5000) ){ 
      fillAll(RgbColor(0));  // hide all when 5s no moving
    }else {    // to show which face is on
      showFace(v);    
    }
    
  } 
  // not found face on top
  else if(!started & millis()> (lastFaceTime + 5000) ){ 
      fillAll(RgbColor(0));  // hide all when 5s no moving
  }

  doFade();
  
  delay(10);
}